sub config_to_command {
    my ( $storecfg, $vmid, $conf, $defaults, $hash_pwd, $ignore, $force, $is_recover,
        $conf_nowrite, $migratedfrom, $statefile) = @_;

    my $cmd          = [];
    my $globalFlags  = [];
    my $machineFlags = [];
    my $rtcFlags     = [];
    my $devices      = [];
    my $pciaddr      = '';
    my $bridges      = {};
    my $kvmver       = kvm_user_version();
    my $vernum       = 0;                    # unknown
    if ( $kvmver =~ m/^(\d+)\.(\d+)$/ ) {
        $vernum = $1 * 1000000 + $2 * 1000;
    }
    elsif ( $kvmver =~ m/^(\d+)\.(\d+)\.(\d+)$/ ) {
        $vernum = $1 * 1000000 + $2 * 1000 + $3;
    }

    my_die "检测到旧版本kvm二进制文件（%s）。", $kvmver if $vernum < 15000;

    my $have_ovz = -f '/proc/vz/vestat';

    push @$cmd, '/usr/bin/kvm';

    push @$cmd, '-id', $vmid;

    my $use_virtio = 0;

    my $qmpsocket = qmp_socket($vmid);
    push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server,nowait";
    push @$cmd, '-mon',     "chardev=qmp,mode=control";

    #检查bios信息
    my $path = check_bios_modify( $conf, $migratedfrom ? 0 : $statefile );

    #只有带内存数据文件开机,才会涉及到bios参数
    if ($path) {
        push @$cmd, '-bios', $path;
    }
    # my $socket = vnc_socket($vmid);
    # push @$cmd, '-vnc', "unix:$socket,x509,password";

    push @$cmd, '-vnc', ":0,websocket,to=200";

    push @$cmd, '-enable-kvm';

    push @$cmd, '-pidfile', pidfile_name($vmid);

    push @$cmd, '-daemonize';

    #todo stub
    $conf = predeal_config_to_command( $vmid, $conf, $is_recover ) unless $conf_nowrite;

    #my $cfgstorage_path = get_cfgstorage_path($storecfg, $conf, $conf->{host}, 'nocreate');
    #push @$cmd, '-d', "guest_errors";
    #push @$cmd, '-D', "$cfgstorage_path/$vmid\.log";

    my $os_type  = &$dist_os_type( $conf->{ ostype } );
    my $use_usb2 = 0;
    for ( my $i = 0 ; $i < $MAX_USB_DEVICES ; $i++ ) {
        next if !$conf->{ "usb$i" };
        $use_usb2 = 1;
    }

    # include usb device config
    push @$devices, '-readconfig', '/sf/share/qemu-server/vtp-usb.cfg' if $use_usb2;

    #usb tablet设备不使用，减少虚拟机的资源空耗
    # enable absolute mouse coordinates (needed by vnc)
    my $tablet = defined( $conf->{ tablet } ) ? $conf->{ tablet } : $defaults->{ tablet };
    if ($tablet) {
        if ($use_usb2) {

            #push @$devices, '-device', 'usb-tablet,bus=ehci.0,port=6';
        }
        else {

            #push @$devices, '-usbdevice', 'tablet';
            push @$devices, '-usb';
        }
    } ## end if ($tablet)

    # host pci devices
    for ( my $i = 0 ; $i < $MAX_HOSTPCI_DEVICES ; $i++ ) {
        my $d = parse_hostpci( $conf->{ "hostpci$i" } );
        next if !$d;
        $pciaddr = print_pci_addr( "hostpci$i", $bridges );
        push @$devices, '-device', "pci-assign,host=$d->{pciid},id=hostpci$i$pciaddr";
    } ## end for ( my $i = 0 ; $i < ...)

    # usb devices
    for ( my $i = 0 ; $i < $MAX_USB_DEVICES ; $i++ ) {
        my $d = parse_usb_device( $conf->{ "usb$i" } );
        next if !$d;
        if ( $d->{ vendorid } && $d->{ productid } ) {
            push @$devices, '-device',
              "usb-host,vendorid=0x$d->{vendorid},productid=0x$d->{productid}";
        }
        elsif ( defined( $d->{ hostbus } ) && defined( $d->{ hostport } ) ) {
            push @$devices, '-device', "usb-host,hostbus=$d->{hostbus},hostport=$d->{hostport}";
        }
    } ## end for ( my $i = 0 ; $i < ...)

    # serial devices
    #changed by wtf
    for ( my $i = 0 ; $i < $MAX_SERIAL_PORTS ; $i++ ) {
        next if ( !exists $conf->{ "serial$i" } );
        my $d = parse_serial( $conf->{ "serial$i" } );
        next if !$d;

        #my $serialdev = print_serialdev_full($d, "serial$i");
        #push @$devices, '-chardev', $serialdev;
        #push @$devices, '-device', "isa-serial,chardev=serial$i";
        push @$cmd, '-serial', "unix:/var/run/serial\_$vmid\_$i.sock,server,nowait";
    } ## end for ( my $i = 0 ; $i < ...)

    my $vmname = $conf->{ name } || "vm$vmid";

    push @$cmd, '-name', $vmname;

    my $sockets = 1;
    $sockets = $conf->{ smp }     if $conf->{ smp };       # old style - no longer iused
    $sockets = $conf->{ sockets } if $conf->{ sockets };

    my $cores = $conf->{ cores } || 1;

    push @$cmd, '-smp', "sockets=$sockets,cores=$cores";

    #[windows] -cpu core2duo,hv_spinlocks=0xfffffff,hv_relaxed
    #[linux]  -cpu core2duo
    if ( $conf->{ cpu } ) {
        my $attr = "$conf->{ cpu }";

#fix BUG 32177 安装centos7.0的图形界面，不能正常进入操作系统
#CPU model表明core2duo型号不支持sse4.1，而我们通过+sse4.1让core2duo支持sse4.1，会导致虚拟机的GNOME报错。
#这里所有的linux系统都不启用sse4.1，windows才启用
        $attr .= ",hv_spinlocks=0xffff,hv_relaxed,hv_time,hv_vapic,+sse4.1"
          if ( $os_type eq $os_type_windows );
        $attr .= ',+sse4.2,+x2apic,+pcid,+pdcm,+xtpr,+ht,+ss,+acpi,+ds';
        push @$cmd, '-cpu', $attr;
    } ## end if ( $conf->{ cpu } )

    push @$cmd, '-nodefaults';

    #加软驱
    if ( $conf->{ floppy } ) {
        my $floppy = "index=0,if=floppy" . print_floppy( $storecfg, $vmid, $conf->{ floppy } );
        push @$cmd, '-drive', "$floppy";
    }

    my $bootorder = $conf->{ boot } || $confdesc->{ boot }->{ default };

    my $bootindex_hash = {};
    my $i              = 1;

    #    foreach my $o ( split( /,/, $bootorder ) ) {
    foreach my $o ( split( //, $bootorder ) ) {
        next if !$o;
        $bootindex_hash->{ $o } = $i * 100;
        $i++;
    }

    #fix bug 13367 by wtf
    #push @$cmd, '-no-acpi' if defined($conf->{acpi}) && $conf->{acpi} == 0;

    push @$cmd, '-no-reboot' if defined( $conf->{ reboot } ) && $conf->{ reboot } == 0;

    my $spice_param_set = {};
    eval {
       $spice_param_set = get_spice_setting( $conf->{ ostype } );
    };

    if (@$ || !%$spice_param_set) {
        lwarn("read spice setting failed, use the default setting!");
    }

    validate_spice_param($spice_param_set, get_default_spice_setting($conf->{ ostype }));
    ldebug(Dumper($spice_param_set));
    set_spice_param($conf, $cmd, $spice_param_set);

#add by cfq,给acpi添加一个表
#my $acpiarg = 'sig=MFTR,rev=1,oem_id=SNGF,oem_table_id=VT,oem_rev=1,asl_compiler_id=FANG,asl_compiler_rev=1';
#push @$cmd, '-acpitable', $acpiarg;

    # time drift fix
    my $tdf = defined( $conf->{ tdf } ) ? $conf->{ tdf } : $defaults->{ tdf };

    my $nokvm = defined( $conf->{ kvm } ) && $conf->{ kvm } == 0 ? 1 : 0;
    my $useLocaltime = $conf->{ localtime };

    if ( my $ost = $conf->{ ostype } ) {

        # other, wxp, w2k, w2k3, w2k8, wvista, win7, win8, win10, l24, l26

        if ( $ost =~ m/^w/ || $ost eq 'sslvpn' ) {    # windows
            $useLocaltime = 1 if !defined( $conf->{ localtime } );

            # use time drift fix when acpi is enabled
            #fix bug 13367 by wtf
            #if (!(defined($conf->{acpi}) && $conf->{acpi} == 0))
            {
                $tdf = 1 if !defined( $conf->{ tdf } );
            }
        } ## end if ( $ost =~ m/^w/ || ...)

        #wtf
        #所有类型的虚拟机都禁用hpet  by zq
        push @$globalFlags, 'kvm-pit.lost_tick_policy=discard';

        push @$cmd, '-no-hpet';

    } ## end if ( my $ost = $conf->...)

    push @$rtcFlags, 'driftfix=slew';

    push @$rtcFlags, 'clock=rt';    #add by zq,虚拟机运行中不跟host机进行时间同步

    if ($nokvm) {
        push @$machineFlags, 'accel=tcg';
    }
    else {

        #my_die "物理CPU不支持虚拟化。" if !$cpuinfo->{ hvm };
    }

    #fix bug by wtf
    #if ($conf->{startdate}) {
    #push @$rtcFlags, "base=$conf->{startdate}";
    #} elsif ($useLocaltime) {
    if ($useLocaltime) {
        push @$rtcFlags, 'base=localtime';
    }

    push @$cmd, '-S' if $conf->{ freeze };

    # set keyboard layout
    my $kb = $conf->{ keyboard } || $defaults->{ keyboard };
    push @$cmd, '-k', $kb if $kb;

    # enable sound
    #my $soundhw = $conf->{soundhw} || $defaults->{soundhw};
    #push @$cmd, '-soundhw', 'es1370';
    #push @$cmd, '-soundhw', $soundhw if $soundhw;

    my $soundhw = $spice_param_set->{ "audio" };
    push @$cmd, '-soundhw', $soundhw if $soundhw;
    if ( $conf->{ agent } ) {
        my $qgasocket = qga_socket($vmid);
        my $pciaddr = print_pci_addr( "qga0", $bridges );
        push @$devices, '-chardev', "socket,path=$qgasocket,server,nowait,id=qga0";
        push @$devices, '-device',  "virtio-serial,id=qga0$pciaddr";
        push @$devices, '-device',  'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
    } ## end if ( $conf->{ agent } )

    $pciaddr = print_pci_addr( "balloon0", $bridges );
    push @$devices, '-device', "virtio-balloon-pci,id=balloon0$pciaddr" if $conf->{ balloon };

    if ( $conf->{ watchdog } ) {
        my $wdopts = parse_watchdog( $conf->{ watchdog } );
        $pciaddr = print_pci_addr( "watchdog", $bridges );
        my $watchdog = $wdopts->{ model } || 'i6300esb';
        push @$devices, '-device', "$watchdog$pciaddr";
        push @$devices, '-watchdog-action', $wdopts->{ action } if $wdopts->{ action };
    } ## end if ( $conf->{ watchdog...})

    my $vollist        = [];
    my $scsicontroller = {};
    my $ahcicontroller = {};
    my $scsihw         = defined( $conf->{ scsihw } ) ? $conf->{ scsihw } : $defaults->{ scsihw };

    my $have_cd = undef;
    foreach_drive(
        $conf,
        sub {
            my ( $ds, $drive ) = @_;

            $use_virtio = 1 if $ds =~ m/^virtio/;

            if ( drive_is_cdrom($drive) ) {

                $have_cd->{ value } = 1;

                if ( $bootindex_hash->{ d } ) {
                    $drive->{ bootindex } = $bootindex_hash->{ d };
                    $bootindex_hash->{ d } += 1;
                }

            } ## end if ( drive_is_cdrom($drive...))
            else {

                if ( $bootindex_hash->{ c } ) {
                    $drive->{ bootindex } = $bootindex_hash->{ c }
                      if $conf->{ bootdisk } && ( $conf->{ bootdisk } eq $ds );
                    $bootindex_hash->{ c } += 1;
                }

                if ( !defined( $conf->{ use_default_cachemode } )
                    || $conf->{ use_default_cachemode } )
                {
                    $drive->{ cache } = "none";
                }

                # add by lbc: td 22447
                if (   $drive->{ backing_file }
                    && defined $conf->{ volatile }
                    && $conf->{ volatile } eq 'yes' )
                {
                    $drive->{ cache } = "writeback";
                } ## end if ( $drive->{ backing_file...})

                # end add
            } ## end else [ if ( drive_is_cdrom($drive...))]
            if ( $drive->{ interface } eq 'scsi' ) {

                my $maxdev = ( $scsihw ne 'lsi' ) ? 256 : 7;
                my $controller = int( $drive->{ index } / $maxdev );
                $pciaddr = print_pci_addr( "scsihw$controller", $bridges );
                push @$devices, '-device', "$scsihw,id=scsihw$controller$pciaddr"
                  if !$scsicontroller->{ $controller };
                $scsicontroller->{ $controller } = 1;
            } ## end if ( $drive->{ interface...})

      #            if ( $drive->{ interface } eq 'sata' ) {
      #                my $controller = int( $drive->{ index } / $MAX_SATA_DISKS );
      #                $pciaddr = print_pci_addr( "ahci$controller", $bridges );
      #                push @$devices, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr"
      #                  if !$ahcicontroller->{ $controller };
      #                $ahcicontroller->{ $controller } = 1;
      #            } ## end if ( $drive->{ interface...})
            if ( defined($hash_pwd) && !%$hash_pwd ) {
                my $info = 0;
                my $drive_val =
                  print_drive_full( $storecfg, $vmid, $drive, undef, undef, \$info, $ignore,
                    $force );
                if ( defined($drive_val) && $drive_val ) {
                    if ($info) {

                        $drive_val .= ",rnd=******";    ##假密码
                    }
                    push @$devices, '-drive', $drive_val;
                    push @$devices, '-device',
                      print_drivedevice_full( $storecfg, $conf, $vmid, $drive, $bridges );
                } ## end if ( defined($drive_val...))
            } ## end if ( defined($hash_pwd...))
            else {
                my $id         = "drive-" . $drive->{ interface } . $drive->{ index };
                my $passwd_str = undef;
                if ( exists $hash_pwd->{ $id } ) {
                    $passwd_str = $hash_pwd->{ $id };
                }
                elsif ( exists $hash_pwd->{ 'drive-default' } ) {
                    $passwd_str = $hash_pwd->{ 'drive-default' };
                }
                else {
                    foreach my $v ( keys %$hash_pwd ){
                        $passwd_str = $hash_pwd->{ $v };
                        last;
                    }
                    #todo stub
                    my $print_passwd = VTP::Tools::print_password($hash_pwd);
                    linfo "not encrypted disk id, drive: $id, hash_pwd: $print_passwd";
                    linfo "now get passwd is:passwd_str=$passwd_str";
                }

                my $info = 0;
                my $drive_val =
                  print_drive_full( $storecfg, $vmid, $drive, undef, $passwd_str, \$info );
                if ( defined($drive_val) && $drive_val ) {
                    if ($info) {

                        #$drive_val .= ",password=$passwd_str";
                        $drive_val .= ",rnd=$passwd_str";
                    }

                    #linfo "drive_val: $drive_val";
                    push @$devices, '-drive', $drive_val;
                    push @$devices, '-device',
                      print_drivedevice_full( $storecfg, $conf, $vmid, $drive, $bridges );
                } ## end if ( defined($drive_val...))
            } ## end else [ if ( defined($hash_pwd...))]
            #todo stub
            if ( VTP::Storage::parse_volume_id( $drive->{ file }, 1 ) ) {
                push @$vollist, $drive->{ file }
                  if (!drive_is_cdrom($drive)
                    || print_drive_full( $storecfg, $vmid, $drive, 'test' ) );
            }
        }
    );

    #if exist /cfs/vmboot.ini, read [boot] [splashtime] as value of splash-time
    my $bootoption = undef;
    $bootoption = "menu=off,splash-time=1";

    #TD34489  加入了可配置bios的选项
    if ( exists $conf->{ splash_time } && $conf->{ splash_time } ) {

        #配置的值大于0时，才将menu=on  需转换为毫秒
        $bootoption = "menu=on,splash-time=" . ( $conf->{ splash_time } * 1000 );
    }

    if ( -e '/cfs/vmboot.ini' ) {
        my %ini;
        tie %ini, 'Config::IniFiles', ( -file => '/cfs/vmboot.ini' );

        my $splashtime = $ini{ boot }{ splashtime };
        $splashtime = 0 if !defined($splashtime);
        $bootoption = "menu=on,splash-time=$splashtime";
    } ## end if ( -e '/cfs/vmboot.ini')
        #else {
        #    if ( $have_cd->{ value } ) {
        #        $bootoption = "menu=on,splash-time=8000";
        #    }
        #} ## end else [ if ( -e '/cfs/vmboot.ini')]
        #if ( VTP::Product::is_vmp_for_vs() ) {
        #    $bootoption = "menu=off,splash-time=1";
        #}

    #reboot-osdirect,reboot-osoffset,reboot-osenter
    $bootoption = $bootoption . ',reboot-osdirect=' . $conf->{ osdirect }
      if ( defined( $conf->{ osdirect } ) );
    $bootoption = $bootoption . ',reboot-osoffset=' . $conf->{ osoffset }
      if ( defined( $conf->{ osoffset } ) );
    $bootoption = $bootoption . ',reboot-osenter=' . $conf->{ osenter }
      if ( defined( $conf->{ osenter } ) );
    push @$cmd, '-boot', $bootoption;

    push @$cmd, '-m', $conf->{ memory } || $defaults->{ memory };

    my $virtio_net = get_virtio_net($vmid);
    for ( my $i = 0 ; $i < $MAX_NETS ; $i++ ) {
        next if !$conf->{ "net$i" };
        my $d = parse_net( $conf->{ "net$i" } );
        next if !$d;

        #check_vswitch( $conf->{ "net$i" } );
        $use_virtio = 1 if $d->{ model } eq 'virtio';

        if ( $bootindex_hash->{ n } ) {
            $d->{ bootindex } = $bootindex_hash->{ n };
            $bootindex_hash->{ n } += 1;
        }

        my $netdevfull = print_netdev_full( $vmid, $conf, $d, "net$i" );
        push @$devices, '-netdev', $netdevfull;

        my $netdevicefull =
          print_netdevice_full( $vmid, $conf, $d, "net$i", $bridges, $virtio_net );
        push @$devices, '-device', $netdevicefull;
    } ## end for ( my $i = 0 ; $i < ...)

    #bridges
    while ( my ( $k, $v ) = each %$bridges ) {
        $pciaddr = print_pci_addr("pci.$k");
        unshift @$devices, '-device', "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr" if $k > 0;
    }

    # hack: virtio with fairsched is unreliable, so we do not use fairsched
    # when the VM uses virtio devices.
    if ( !$use_virtio && $have_ovz ) {

        my $cpuunits =
          defined( $conf->{ cpuunits } ) ? $conf->{ cpuunits } : $defaults->{ cpuunits };

        push @$cmd, '-cpuunits', $cpuunits if $cpuunits;

        # fixme: cpulimit is currently ignored
        #push @$cmd, '-cpulimit', $conf->{cpulimit} if $conf->{cpulimit};
    } ## end if ( !$use_virtio && $have_ovz)

    # add custom args
    if ( $conf->{ args } ) {
        #todo stub
        my $aa = VTP::Tools::split_args( $conf->{ args } );
        push @$cmd, @$aa;
    }

    push @$cmd, @$devices;
    push @$cmd, '-rtc', join( ',', @$rtcFlags )
      if scalar(@$rtcFlags);
    push @$cmd, '-machine', join( ',', @$machineFlags )
      if scalar(@$machineFlags);
    push @$cmd, '-global', join( ',', @$globalFlags )
      if scalar(@$globalFlags);

    #add by zq, 禁用bios的s3和s4支持
    push @$cmd, '-global', 'PIIX4_PM.disable_s3=1';

  #gwl 启用s4支持
  #gwl ssl驱动有bug，会导致蓝屏，已找到原因，无人力修改，暂时先禁掉休眠
    push @$cmd, '-global', 'PIIX4_PM.disable_s4=1';

    #add by gzy, 添加虚拟串口设备，以便Host、Guest间通信
    afterdeal_config_to_command( $vmid, $conf, $cmd );

# TD33960 添加了双显卡后导致原有设备的pci位置变化，驱动重装，移动到启动参数末尾
    if ( $conf->{ ostype } && ( $conf->{ ostype } =~ m/wxp/ || $conf->{ ostype } =~ m/win7/ || $conf->{ostype} =~ m/win10/ ) ) {
        if (   defined $conf->{ double_screen }
            && $conf->{ double_screen }
            && $conf->{ ostype } ne "sslvpn" )
        {
            push @$cmd, '-device', 'qxl';
        } ## end if ( defined $conf->{ ...})
        elsif ( ( !defined $conf->{ double_screen } )
            && $conf->{ ostype } ne "sslvpn" )
        {
            push @$cmd, '-device', 'qxl';
        }
    } ## end if ( $conf->{ ostype }...)

    set_mem_mana_para($conf, $cmd);

    return wantarray ? ( $cmd, $vollist ) : $cmd;
} ## end sub config_to_command

sub qmp_socket {
    my ($vmid) = @_;
    return "${var_run_tmpdir}/$vmid.qmp";
}

sub pidfile_name {
    my ($vmid) = @_;
    return "${var_run_tmpdir}/$vmid.pid";
}

sub check_bios_modify {
    my ( $conf, $statefile ) = @_;
    return $conf->{ biosversion } if ( exists $conf->{ biosversion } );
    return undef if $statefile;
    return $BIOS;
} ## end sub check_bios_modify

sub predeal_config_to_command {
    my ( $vmid, $conf, $is_recover ) = @_;

    #派生虚拟机且设置为重启还原，需维持其同模板的半虚拟化配置一致
    deal_volatile_virtio_conflict( $vmid, $conf ) if !$is_recover;

    #根据配置设置真实的半虚拟化启用情况
    #注意：回滚快照和恢复挂起不应该改变半虚拟化的设置
    set_real_virtio( $vmid, $conf ) if !$is_recover;

#1. 虚拟机未安装virtio_tool，且未挂载其他cd-rom，默认为虚拟机挂载virtio_tool安装包

    #todo stub
    my $virtio_tool_path = VTP::QemuServer::get_vtool_iso_path( $vmid, $conf );
    $conf = config_virtiotool_mnt_default( $vmid, $conf, $virtio_tool_path );

    #2. 虚拟机安装了virtio_tool,但磁盘、网卡均未启用半虚拟化，什么也不做
    return $conf;
} ## end sub predeal_config_to_command

# @note 根据操作系统类型值判断是windows还是linux
# $os_type 配置文件中os_type字段值
# return  windows -windows系统；linux -linux系统；other -其他
my $dist_os_type = sub {
    my $os_type = shift;

    my $os_types_map = os_list_description();
    my $os_name = $os_types_map->{ $os_type // "other" };

    my $type = "Other";
    return $type if ( !$os_name );

    if ( $os_name =~ /windows/i ) {
        $type = $os_type_windows;
    }
    elsif ( $os_name =~ /linux/i ) {
        $type = $os_type_linux;
    }

    return $type;
};

sub os_list_description {

    #changed by wtf
    return {
        other   => 'Other',
        other64 => 'Other64',
        wxp     => 'Windows XP',
        wxp64   => 'Windows XP 64',

        ws03   => 'Windows 2003',
        ws0364 =>,
        'Windows 2003 64',
        ws08   => 'Windows 2008',
        ws0864 =>,
        'Windows 2008 64',

        #w2k => 'Windows 2000',
        #w2k3 =>, 'Windows 2003',
        #w2k8 => 'Windows 2008',
        #wvista => 'Windows Vista',
        win7   => 'Windows 7',
        win764 => 'Windows 7 64',
        win8   => 'Windows 8',
        win864 => 'Windows 8 64',
        win10  => 'Windows 10',
        win1064 => 'Windows 10 64',
        l24    => 'Linux 2.4',
        l2464  => 'Linux 2.4 64',
        l26    => 'Linux 2.6',
        l2664  => 'Linux 2.6 64',
        w2k    => 'Windows 2000',
        ws1264 => 'Windows 2012',
    };
} ## end sub os_list_description

sub parse_hostpci {
    my ($value) = @_;

    return undef if !$value;

    my $res = {};

    if ( $value =~ m/^[a-f0-9]{2}:[a-f0-9]{2}\.[a-f0-9]$/ ) {
        $res->{ pciid } = $value;
    }
    else {
        return undef;
    }

    return $res;
} ## end sub parse_hostpci

sub print_pci_addr {
    my ( $id, $bridges, $virtio_net ) = @_;

    my $res     = '';
    my $devices = get_pci_devices();

    if ( defined( $devices->{ $id }->{ bus } ) && defined( $devices->{ $id }->{ addr } ) ) {

        #xhz --virtio指定网卡bus, addr
        my $virt = get_new_bus_addr( $virtio_net, $devices->{ $id } );
        if ( defined $virt ) {
            $res = ",bus=pci.$virt->{bus},addr=$virt->{addr}";
            $bridges->{ $virt->{ bus } } = 1 if $bridges;
        }
        else {
            my $addr = sprintf( "0x%x", $devices->{ $id }->{ addr } );
            my $bus = $devices->{ $id }->{ bus };
            $res = ",bus=pci.$bus,addr=$addr";
            $bridges->{ $bus } = 1 if $bridges;
        } ## end else [ if ( defined $virt ) ]
    } ## end if ( defined( $devices...))
    return $res;

} ## end sub print_pci_addr

sub get_pci_devices {
    my $devices = {

        #addr1 : ide,parallel,serial (motherboard)
        #addr2 : first videocard
        balloon0 => { bus => 0, addr => 3 },
        watchdog => { bus => 0, addr => 4 },
        scsihw0  => { bus => 0, addr => 5 },
        scsihw1  => { bus => 0, addr => 6 },
        ahci0    => { bus => 0, addr => 7 },
        qga0     => { bus => 0, addr => 8 },
        virtio0  => { bus => 0, addr => 10 },
        virtio1  => { bus => 0, addr => 11 },
        virtio2  => { bus => 0, addr => 12 },
        virtio3  => { bus => 0, addr => 13 },
        virtio4  => { bus => 0, addr => 14 },
        virtio5  => { bus => 0, addr => 15 },
        hostpci0 => { bus => 0, addr => 16 },
        hostpci1 => { bus => 0, addr => 17 },
        net0     => { bus => 0, addr => 18 },
        net1     => { bus => 0, addr => 19 },
        net2     => { bus => 0, addr => 20 },
        net3     => { bus => 0, addr => 21 },
        net4     => { bus => 0, addr => 22 },
        net5     => { bus => 0, addr => 23 },

        #addr29 : usb-host (vtp-usb.cfg)
        'pci.1'    => { bus => 0, addr => 30 },
        'pci.2'    => { bus => 0, addr => 31 },
        'net6'     => { bus => 1, addr => 1 },
        'net7'     => { bus => 1, addr => 2 },
        'net8'     => { bus => 1, addr => 3 },
        'net9'     => { bus => 1, addr => 4 },
        'net10'    => { bus => 1, addr => 5 },
        'net11'    => { bus => 1, addr => 6 },
        'net12'    => { bus => 1, addr => 7 },
        'net13'    => { bus => 1, addr => 8 },
        'net14'    => { bus => 1, addr => 9 },
        'net15'    => { bus => 1, addr => 10 },
        'net16'    => { bus => 1, addr => 11 },
        'net17'    => { bus => 1, addr => 12 },
        'net18'    => { bus => 1, addr => 13 },
        'net19'    => { bus => 1, addr => 14 },
        'net20'    => { bus => 1, addr => 15 },
        'net21'    => { bus => 1, addr => 16 },
        'net22'    => { bus => 1, addr => 17 },
        'net23'    => { bus => 1, addr => 18 },
        'net24'    => { bus => 1, addr => 19 },
        'net25'    => { bus => 1, addr => 20 },
        'net26'    => { bus => 1, addr => 21 },
        'net27'    => { bus => 1, addr => 22 },
        'net28'    => { bus => 1, addr => 23 },
        'net29'    => { bus => 1, addr => 24 },
        'net30'    => { bus => 1, addr => 25 },
        'net31'    => { bus => 1, addr => 26 },
        'virtio6'  => { bus => 2, addr => 1 },
        'virtio7'  => { bus => 2, addr => 2 },
        'virtio8'  => { bus => 2, addr => 3 },
        'virtio9'  => { bus => 2, addr => 4 },
        'virtio10' => { bus => 2, addr => 5 },
        'virtio11' => { bus => 2, addr => 6 },
        'virtio12' => { bus => 2, addr => 7 },
        'virtio13' => { bus => 2, addr => 8 },
        'virtio14' => { bus => 2, addr => 9 },
        'virtio15' => { bus => 2, addr => 10 },
    };
    return $devices;
} ## end sub get_pci_devices

sub get_pci_addr {
    my ($id) = @_;
    my $devices = get_pci_devices();
    return $devices->{ $id };
}

sub get_new_bus_addr {
    my ( $virtio_net, $device ) = @_;

    foreach my $virt (@$virtio_net) {
        if (   $virt->{ old }->{ bus } == $device->{ bus }
            && $virt->{ old }->{ addr } == $device->{ addr } )
        {
            my $ret = {};
            $ret->{ bus }  = $virt->{ new }->{ bus };
            $ret->{ addr } = $virt->{ new }->{ addr };
            return $ret;
        } ## end if ( $virt->{ old }->{...})
    } ## end foreach my $virt (@$virtio_net)
    return undef;
} ## end sub get_new_bus_addr

sub parse_usb_device {
    my ($value) = @_;

    return undef if !$value;

    my @dl = split( /,/, $value );
    my $found;

    my $res = {};
    foreach my $v (@dl) {
        if ( $v =~ m/^host=(0x)?([0-9A-Fa-f]{4}):(0x)?([0-9A-Fa-f]{4})$/ ) {
            $found              = 1;
            $res->{ vendorid }  = $2;
            $res->{ productid } = $4;
        }
        elsif ( $v =~ m/^host=(\d+)\-(\d+(\.\d+)*)$/ ) {
            $found             = 1;
            $res->{ hostbus }  = $1;
            $res->{ hostport } = $2;
        }
        else {
            return undef;
        }
    } ## end foreach my $v (@dl)

    return $res;
} ## end sub parse_usb_device

sub parse_serial {
    my ($value) = @_;
    if ( $value && $value =~ 'none' ) {
        return {
            vmid => undef,
            port => undef,

            #            vmname => undef,
            #            node   => undef,
        };
    } ## end if ( $value && $value ...)

    #    if ( !$value || $value !~ m/(\d+)?,([0-3])?,([^,]+)?,(\S+)?/ ) {
    ##xhz if ( !$value || $value !~ m/(\d+),([0-3])/ ) {
    if ( !$value || $value !~ m/(\d+),([0-1])/ ) {
        return undef;
    }
    my $vmid = $1;
    my $port = $2;

    # change by lyf, no need Encode::encode, it's quite annoy
    #    my $vmname = $3;
    #    my $node   = $4;
    if ( $vmid && defined($port) )    #&& $vmname && $node

      #        || !$vmid && !defined($port) && !$vmname && !$node )
    {
        # todo stub
        return undef if ( !VTP::JSONSchema::vtp_verify_vmid( $vmid, 'noerr' ) );

        #        return undef if ( !VTP::JSONSchema::vtp_verify_node_name( $node, 'noerr' ) );
        #        return undef if ( !VTP::JSONSchema::vtp_verify_vms_name( $vmname, 'noerr' ) );
        my $data = {};
        $data->{ vmid } = $vmid;
        $data->{ port } = $port;

        #        $data->{ vmname } = $vmname;
        #        $data->{ node }   = $node;
        return $data;
    } ## end if ( $vmid && defined(...))

} ## end sub parse_serial

sub print_floppy {
    my ( $storecfg, $vmid, $data ) = @_;
    my $path = get_iso_path( $storecfg, $vmid, $data );
    my_die "软驱（%s）不存在！", $path if ( $path && !-f $path );
    $path = ",file=$path" if $path;
    return $path;
} ## end sub print_floppy

sub get_iso_path {
    my ( $storecfg, undef, $cdrom ) = @_;

    unless ( defined $storecfg && %$storecfg ) {
        my_die "系统异常，请重试。如果仍然出现该错误，请保留现场并联系深信服技术支持！";
    }
    unless ( defined $cdrom && $cdrom ne "" ) {
        my_die "系统异常，请重试。如果仍然出现该错误，请保留现场并联系深信服技术支持！";
    }

    if ( $cdrom eq 'cdrom' ) {
        return get_cdrom_path();
    }
    elsif ( $cdrom eq 'none' ) {
        return '';
    }
    elsif ( $cdrom =~ m|^/| ) {
        return $cdrom;
    }
    else {
        # todo stub
        my $path = VTP::Storage::path_iso( $storecfg, $cdrom );
        return ( -e "$path" ) ? $path : '';
    }
} ## end sub get_iso_path

sub get_cdrom_path {

    return $cdrom_path if $cdrom_path;

    return $cdrom_path = "/dev/cdrom"  if -l "/dev/cdrom";
    return $cdrom_path = "/dev/cdrom1" if -l "/dev/cdrom1";
    return $cdrom_path = "/dev/cdrom2" if -l "/dev/cdrom2";
} ## end sub get_cdrom_path

sub check_spice_param {
    my ( $value, $range_beg, $range_end ) = @_;
    my $ret = 1;
    if ( $value < $range_beg || $value > $range_end ) {
        $ret = 0;
    }
    return $ret;
} ## end sub check_spice_param

sub validate_spice_param {
    my ($set, $def_set) = @_;

    $set->{ "audio" }      = $set->{ "audio" } // $def_set->{ "audio" };
    $set->{ "up_port" }    = $set->{ "up_port" } // $def_set->{ "up_port" };
    $set->{ "down_port" }  = $set->{ "down_port" } // $def_set->{ "down_port" };
    $set->{ "usb_num" }    = $set->{ "usb_num" } // $def_set->{ "usb_num" };
    $set->{ "jpeg_type" }  = $set->{ "jpeg_type" } // $def_set->{ "jpeg_type" };
    $set->{ "jpeg_quality" }    = $set->{ "jpeg_quality" } // $def_set->{ "jpeg_quality" };
    $set->{ "camera_num" } = $set->{ "camera_num" } // $def_set->{ "camera_num" };


    if ( !check_spice_param( $set->{ "jpeg_quality" }, 1, 100 ) ) {
        $set->{ "jpeg_quality" } = $def_set->{ "jpeg_quality" };
    }

    if ( $set->{ "jpeg_type" } !~ m/^(auto|never|always)$/ ) {
        $set->{ "jpeg_type" } = $def_set->{ "jpeg_type" };
    }

    if ( $set->{ "audio" } !~ m/^(ac97|hda)$/ ) {
        $set->{ "audio" } = $def_set->{ "audio" };
    }

    if ( !check_spice_param( $set->{ "usb_num" } + $set->{ "camera_num" }, 0, 15 ) ) {
        $set->{ "usb_num" } = $def_set->{ "usb_num" };
        $set->{ "camera_num" } = $def_set->{ "camera_num" };
    }


    if ( !check_spice_param( $set->{ "up_port" }, 0, 65535 ) ) {
        $set->{ "up_port" } = $def_set->{ "up_port" };
    }

    if ( !check_spice_param( $set->{ "down_port" }, 0, 65535 ) ) {
        $set->{ "down_port" }  = $def_set->{ "down_port" };
    }

    if ( $set->{ "up_port" } < $set->{ "down_port" } ) {
        $set->{ "up_port" } = $def_set->{ "up_port" };
        $set->{ "down_port" }  = $def_set->{ "down_port" };
    }
}

sub get_default_spice_setting {
    my ($os_type) = @_;

    my $def_set = {};

    if ( $os_type =~ m/xp/ ) {
        $def_set->{ "audio" } = 'ac97';
    } else {
        $def_set->{ "audio" } = 'hda';
    }

    $def_set->{ "up_port" } = "5699";
    $def_set->{ "down_port" } = "5500";

    $def_set->{ "usb_num" } = "14";
    $def_set->{ "jpeg_type" } = "never";
    $def_set->{ "jpeg_quality" } = "85";
    $def_set->{ "camera_num" } = "1";

    return $def_set;
}

my $spiceconf = "/cfs/VDC/kvm_spice_setting.conf";

sub get_spice_conf {
    return VTP::Tools::file_get_contents($spiceconf);
}

sub set_spice_conf {
    my ($conf) = @_;
    VTP::Tools::file_set_contents($spiceconf, $conf);
}

sub get_spice_setting {
    my ($os_type) = @_;
    my $ret_set = {};

    my $setting = from_json(VTP::Tools::file_get_contents($spiceconf));

    if ($os_type ) {
        $ret_set->{ "audio" } = $setting->{ "audio_type" }->{ $os_type };
    }

    $ret_set->{ "up_port" } = $setting->{ "port_range" }->{ "up_port" };
    $ret_set->{ "down_port" } = $setting->{ "port_range" }->{ "down_port" };
    $ret_set->{ "usb_num" } = $setting->{ "usb_num" }->{ "usb"};
    $ret_set->{ "camera_num" } = $setting->{ "usb_num" }->{ "camera"};
    $ret_set->{ "jpeg_type" } = $setting->{ "jpeg_compress" }->{ "type" };
    $ret_set->{ "jpeg_quality" } = $setting->{ "jpeg_compress" }->{ "quality" };

    return $ret_set;
} ## end sub get_spice_setting

sub set_spice_param {
    my ($conf, $cmd, $spice_param_set) = @_;

    my $up_port      = $spice_param_set->{ "up_port" };
    my $down_port    = $spice_param_set->{ "down_port" };
    my $usb_num      = $spice_param_set->{ "usb_num" };
    my $camera_num   = $spice_param_set->{ "camera_num" };
    my $jpeg_type    = $spice_param_set->{ "jpeg_type" };
    my $jpeg_quality = $spice_param_set->{ "jpeg_quality" };
    my $osname       = $conf->{ ostype } // "unkown";

    #软件xp和win7要添加spice相关的东西
    if ( $conf->{ ostype } && ( $conf->{ ostype } =~ m/wxp/ || $conf->{ ostype } =~ m/win7/ || $conf->{ostype} =~m/win10/ ) ) {
        my $is_adesk = 0;
        eval { $is_adesk = VTP::VMPSN::is_adesk(); };

        #添加spice参数
        push @$cmd, '-device', 'ich9-usb-ehci1,id=usb,bus=pci.0,addr=0x4.0x7';
        push @$cmd, '-device',
          'ich9-usb-uhci1,masterbus=usb.0,firstport=0,bus=pci.0,multifunction=on,addr=0x4';
        push @$cmd, '-device', 'ich9-usb-uhci2,masterbus=usb.0,firstport=2,bus=pci.0,addr=0x4.0x1';
        push @$cmd, '-device', 'ich9-usb-uhci3,masterbus=usb.0,firstport=4,bus=pci.0,addr=0x4.0x2';

        # if ( defined $conf->{support_usb_count} && ($conf->{support_usb_count} == 1))
        # {
        #增加3个controller以支持12个USB1.0设备
        push @$cmd, '-device', 'ich9-usb-uhci4,masterbus=usb.0,firstport=6,bus=pci.0,addr=0x4.0x3';
        push @$cmd, '-device', 'ich9-usb-uhci5,masterbus=usb.0,firstport=8,bus=pci.0,addr=0x4.0x4';
        push @$cmd, '-device', 'ich9-usb-uhci6,masterbus=usb.0,firstport=10,bus=pci.0,addr=0x4.0x5';

        # }

        #服务器版本使用cirrus显卡（否则控制台会出现两个鼠标），其他使用qxl显卡
        push @$cmd, '-vga', ( $is_adesk ? 'qxl' : 'cirrus' );
        push @$cmd, '-spice',
"port=0,disable-ticketing,up_port=$up_port,down_port=$down_port,os_type=$osname,jpeg-wan-compression=$jpeg_type,jpeg_quality=$jpeg_quality,streaming-video=filter";

        push @$cmd, '-device',  'virtio-serial-spice,max_ports=2,ioeventfd=off';
        push @$cmd, '-chardev', 'spicevmc,id=vdagent,debug=0,name=vdagent';
        push @$cmd, '-device',  'virtserialport,chardev=vdagent,name=com.redhat.spice.0';

        #添加usb设备
        for ( my $i = 0 ; $i < $usb_num ; $i++ ) {
            push @$cmd, '-chardev', "spicevmc,id=charredir$i,name=usbredir";
            push @$cmd, '-device',  "usb-redir,chardev=charredir$i,id=redir$i";
        }

        #摄像头放在USB后面，避免占用USB1.0的端口
        for ( my $j = 0 ; $j < $camera_num ; $j++ ) {
            push @$cmd, '-chardev', "spicevmc,id=charrd$j,name=usbredir";
            push @$cmd, '-device',  "redir-video,chardev=charrd$j,id=redirrd$j";
        }
    } ## end if ( $conf->{ ostype }...)
    else {
        my $vga = 'cirrus';
        if ( $conf->{ osname } ) {
            if ( $conf->{ osname } =~ m/ubuntu/i ) {
                $vga = 'vmware';
            }
            elsif ( $conf->{ osname } =~ m/centos/i ) {    #禁用kvmclock
                push @$cmd, '-no-kvm-clock';
            }
        } ## end if ( $conf->{ osname })
        push @$cmd, '-vga', $vga;
    } ## end else [ if ( $conf->{ ostype }...)]
}

sub qga_socket {
    my ($vmid) = @_;
    return "${var_run_tmpdir}/$vmid.qga";
}

sub parse_watchdog {
    my ($value) = @_;

    return undef if !$value;

    my $res = {};

    foreach my $p ( split( /,/, $value ) ) {
        next if $p =~ m/^\s*$/;

        if ( $p =~ m/^(model=)?(i6300esb|ib700)$/ ) {
            $res->{ model } = $2;
        }
        elsif ( $p =~ m/^(action=)?(reset|shutdown|poweroff|pause|debug|none)$/ ) {
            $res->{ action } = $2;
        }
        else {
            return undef;
        }
    } ## end foreach my $p ( split( /,/,...))

    return $res;
} ## end sub parse_watchdog

sub foreach_drive {
    my ( $conf, $func ) = @_;

    my @sort_keys = sort( keys %$conf );
    foreach my $ds (@sort_keys) {
        next if !valid_drivename($ds);

        my $drive = parse_drive( $ds, $conf->{ $ds } );
        next if !$drive;
        $drive->{ vmname } = $conf->{ name };
        $drive->{ dir }    = $conf->{ dir } if $conf->{ dir };
        $drive->{ host }   = $conf->{ host };
        &$func( $ds, $drive );

    } ## end foreach my $ds (@sort_keys)

} ## end sub foreach_drive

sub drive_is_cdrom {
    my ($drive) = @_;

    return $drive && $drive->{ media } && ( $drive->{ media } eq 'cdrom' );

} ## end sub drive_is_cdrom

sub print_drive_full {
    my ( $storecfg, $vmid, $drive, $test, $passwd, $info, $ignore, $force ) = @_;
    my $opts = '';
    foreach my $o (@qemu_drive_options) {
        next if $o eq 'bootindex';
        next if $o eq 'cut';
        next if $o eq 'copy';
        next if $o eq 'preallocate';
        next if $o eq 'backing_file';
        next if $o eq 'percent';
        next if $o eq 'encrypted';
        next if $o eq 'md5';
        next if $o eq 'forecast';
        next if $o eq 'cache_size';
        $opts .= ",$o=$drive->{$o}" if $drive->{ $o };
    } ## end foreach my $o (@qemu_drive_options)

    foreach my $o (qw(bps bps_rd bps_wr)) {
        my $v = $drive->{ "m$o" };
        $opts .= ",$o=" . int( $v * 1024 * 1024 ) if $v;
    }

    # use linux-aio by default (qemu default is threads)
    if ( !$drive->{ aio } ) {
        $opts .= ",aio=native";
    }
    else {
        $opts .= ",aio=threads";
    }

    if ( $drive->{ forecast } ) {
        $opts .= ",forecast=$drive->{forecast}";
        $opts .= ",cache_cnt=4096,cache_size=$drive->{cache_size}"
          if ( $drive->{ forecast } eq 'enable' && $drive->{ cache_size } );
    }
    if ( !drive_is_cdrom($drive) && !exists($drive->{serial}) ) {
        $opts .= ",serial=$vmid-$drive->{index}"; #磁盘序列号
    }
    if ( !drive_is_cdrom($drive) && is_local_vm( undef, $vmid ) ) {    #cdrom不用inc_bitmap
        my $drivename = basename( $drive->{ file } );
        my $inc_bitmap = VTP::QemuBackRecovery::get_inc_bitmap( $vmid, $drivename );
        $opts .= ",bitmap_file=$inc_bitmap" if defined($inc_bitmap);
    }

    my $path;
    my $volid = $drive->{ file };
    if ( drive_is_cdrom($drive) ) {
        eval { $path = get_iso_path( $storecfg, $vmid, $volid ); };
        return $path ? 1 : 0 if ($test);
    }
    else {
        if ( $volid =~ m|^/| ) {
            $path = $volid;
        }
        else {
            eval {
                $path = VTP::Storage::path(
                    $storecfg, $volid,
                    $drive->{ vmname },
                    $drive->{ dir },
                    $drive->{ host }
                );
            };

            my $nodeip   = undef;
            eval {
                $nodeip = VTP::Tools::get_local_ip();
            };
            lerror("get_local_ip failed : $@") if ($@);
            ##提前检查镜像是否存在
            if ( !defined($path) || !-e $path ) {
                my $err = "disk path is empty or does not exist!";

                my $conf = VTP::QemuServer::load_config( $vmid, undef, undef, 'noerr' );
                ##判断是否是派生虚拟机
                if ( !defined $conf->{ template } ) {
                    ldebug("the $vmid is not derived vm");
                    my_die($err);
                }
                ##判断是否开启了还原模式
                if ( !$conf->{ volatile } || $conf->{ volatile } ne 'yes' ) {
                    ldebug("the $vmid is not volatile vm");
                    my_die($err);
                }
                ##判断当前镜像是否是增量镜像
                if ( !$drive->{ backing_file } ) {
                    ldebug("the file is not increameng image");
                    my_die($err);
                }
                linfo("$vmid increament drive image is missing, create a new one.");
                eval { create_drive_image( $drive, $storecfg, $conf ); };
                if ( my $errmsg = $@ ) {
                    lerror("create drive image err: $errmsg");
                    my_die($err);
                }
            } ## end if ( !defined($path) ||...)

            ##检查一下虚拟机镜像的格式是否正确
            my $isqcow2 = 1;
            eval { $isqcow2 = VTP::Tools::vdisk_is_qcow2($path); };
            if ( my $err = $@ || !$isqcow2 ) {
                my_die("虚拟机磁盘格式异常，请重试，如果仍然失败，可能是磁盘文件已损坏，如果有异地备份可以选择恢复虚拟机");
            }

            #校磁盘密码，磁盘没有加密则不影响
            $$info = VTP::Tools::vdisk_is_encrypted( $path, $vmid, $nodeip);
            linfo "$path encrypted: $$info";
            if ( $$info && !( defined($ignore) && $ignore == 1 ) ) {
                unless ( defined($passwd) ) {
                    if ( defined($force) && $force ) {
                        return 0;
                    }
                    else {
                        my_die "加密磁盘缺少密码！";
                    }
                } ## end unless ( defined($passwd) )
                my $check_ret = VTP::Tools::check_vdisk_passwd( $path, $passwd, $vmid );
                linfo "check_vdisk_passwd, check_ret: $check_ret";
                my_die "加密磁盘密码错误！" if ( $check_ret == 0 );    ##密码不正确
                my_die "密码格式错误！" if ( $check_ret == 3 );
                if ( $check_ret == 2 ) {
                    ##初始化磁盘密码
                    my $set_ret = "";
                    my $cmd     = "";

                  #TD 34686 旧有模板部署的虚拟机，磁盘初始化密码走原来的流程
                    if ( VTP::Tools::is_disk_encrypted($path) ) {
                        $cmd = "qemu-img password -n $passwd '$path' -i $vmid";
                        eval { $set_ret = VTP::Tools::run_realcmd($cmd); };
                        unless ($set_ret) {
                            linfo
"run_realcmd exec failed, qemu-img password -n ****** '$path' -i $vmid, [$@]";
                            my_die "程序错误！";
                        }
                        unless ( $set_ret =~ m/^.*change password ok.*$/ ) {
                            linfo "qemu-img password -n ****** '$path' -i $vmid, set_ret: $set_ret";
                            my_die "磁盘初始化密码失败！";
                        }
                    } ## end if ( VTP::Tools::is_disk_encrypted...)

                    # 新模板部署的虚拟机，初始化密码的新流程
                    else {
                        init_personal_disk_passwd( $path, $vmid, $passwd );
                    }
                } ## end if ( $check_ret == 2 )
                if ( $check_ret == -1 ) {
                    my_die "未知的错误！";
                }
            } ## end if ( $$info && !( defined...))
        } ## end else [ if ( $volid =~ m|^/| )]
        if ( !$drive->{ cache } && ( $path =~ m|^/dev/| || $path =~ m|\.raw$| ) ) {
            $opts .= ",cache=none";
        }
    } ## end else [ if ( drive_is_cdrom($drive...))]

    if ( !drive_is_cdrom($drive) ) {
        my_die "获取镜像路径失败，虚拟机可能已经损坏！" if ( !$path );
        if ( VTP::Product::is_vmp_for_vs() ) {
            my_die "虚拟磁盘（%s）不存在！", $path if ( !-e $path );
            $path = convert_path_for_vs( $path, $volid, $storecfg );
        }
    } ## end if ( !drive_is_cdrom($drive...))

    my $pathinfo = $path ? "file=$path," : '';

    return "${pathinfo}if=none,id=drive-$drive->{interface}$drive->{index}$opts";
} ## end sub print_drive_full

sub print_netdevice_full {
    my ( $vmid, $conf, $net, $netid, $bridges, $virtio_net ) = @_;

    my $bootorder = $conf->{ boot } || $confdesc->{ boot }->{ default };

    my $device = $net->{ model };
    if ( $net->{ model } eq 'virtio' ) {
        $device = 'virtio-net-pci';
    }

    # qemu > 0.15 always try to boot from network - we disable that by
    # not loading the pxe rom file
    my $extra = ( $bootorder !~ m/n/ ) ? "romfile=," : '';
    my $pciaddr = print_pci_addr( "$netid", $bridges, $virtio_net );
    my $tmpstr = "$device,${extra}mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid";
    $tmpstr .= ",bootindex=$net->{bootindex}" if $net->{ bootindex };
    return $tmpstr;
} ## end sub print_netdevice_full

sub print_netdev_full {
    my ( $vmid, $conf, $net, $netid ) = @_;

    my $i = '';
    if ( $netid =~ m/^net(\d+)$/ ) {
        $i = int($1);
    }

    my_die "虚拟网卡序号（%s）太大。", $i if $i >= ${ MAX_NETS };

    #my $ifname = "${vmid}$i";
    my $ifname = sprintf( "%u%02u", ${ vmid }, $i );

    # kvm uses TUNSETIFF ioctl, and that limits ifname length
    my_die "虚拟接口名称（%s）太长，最大值为15个字符。", $ifname
      if length($ifname) >= 16;

    my $vhostparam = '';
    $vhostparam = ',vhost=on,vhostforce=on' if $kernel_has_vhost_net && $net->{ model } eq 'virtio';

    my $vmname = $conf->{ name } || "vm$vmid";

    if ( defined( $net->{ bridge } ) ) {
        return "type=tap,id=$netid,ifname=${ifname},script=/sf/etc/kvm/vtp-bridge$vhostparam";
    }
    else {
        return "type=user,id=$netid,hostname=$vmname";
    }
} ## end sub print_netdev_full

sub print_drivedevice_full {
    my ( $storecfg, $conf, $vmid, $drive, $bridges ) = @_;

    my $device = '';
    my $maxdev = 0;

    if ( $drive->{ interface } eq 'virtio' ) {
        my $pciaddr = print_pci_addr( "$drive->{interface}$drive->{index}", $bridges );
        $device =
"virtio-blk-pci,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}$pciaddr";
    }
    elsif ( $drive->{ interface } eq 'scsi' ) {
        $maxdev = ( $conf->{ scsihw } && $conf->{ scsihw } ne 'lsi' ) ? 256 : 7;
        my $controller = int( $drive->{ index } / $maxdev );
        my $unit       = $drive->{ index } % $maxdev;
        my $devicetype = 'hd';
        my $path       = '';
        if ( drive_is_cdrom($drive) ) {
            $devicetype = 'cd';
        }
        else {
            if ( $drive->{ file } =~ m|^/| ) {
                $path = $drive->{ file };
            }
            else {
                #todo stub
                $path = VTP::Storage::path(
                    $storecfg,
                    $drive->{ file },
                    $drive->{ vmname },
                    $drive->{ dir },
                    $drive->{ host }
                );
            } ## end else [ if ( $drive->{ file } ...)]

            if ( $path =~ m/^iscsi\:\/\// ) {
                $devicetype = 'generic';
            }
            else {
                $devicetype = 'block' if path_is_scsi($path);
            }
        } ## end else [ if ( drive_is_cdrom($drive...))]

        if ( !$conf->{ scsihw } || $conf->{ scsihw } eq 'lsi' ) {
            $device =
"scsi-$devicetype,bus=scsihw$controller.0,scsi-id=$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}"
              if !$conf->{ scsihw } || $conf->{ scsihw } eq 'lsi';
        }
        else {
            $device =
"scsi-$devicetype,bus=scsihw$controller.0,channel=0,scsi-id=0,lun=$drive->{index},drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
        }

    } ## end elsif ( $drive->{ interface...})
    elsif ( $drive->{ interface } eq 'ide' ) {
        $maxdev = 2;
        my $controller = int( $drive->{ index } / $maxdev );
        my $unit       = $drive->{ index } % $maxdev;
        my $devicetype = ( $drive->{ media } && $drive->{ media } eq 'cdrom' ) ? "cd" : "hd";

        $device =
"ide-$devicetype,bus=ide.$controller,unit=$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
    } ## end elsif ( $drive->{ interface...})

#    elsif ( $drive->{ interface } eq 'sata' ) {
#        my $controller = int( $drive->{ index } / $MAX_SATA_DISKS );
#        my $unit       = $drive->{ index } % $MAX_SATA_DISKS;
#        $device =
#          "ide-drive,bus=ahci$controller.$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
#    } ## end elsif ( $drive->{ interface...})
    elsif ( $drive->{ interface } eq 'usb' ) {
        my_die "该方法尚未实现。";

        #  -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0
    }
    else {
        my_die "不支持的接口类型。";
    }

    $device .= ",bootindex=$drive->{bootindex}" if $drive->{ bootindex };

    return $device;
} ## end sub print_drivedevice_full

#数据格式: 原bus addr信息新bus addr信息
#bus-old,addr-old,bus-new,addr-new
sub get_virtio_net {
    my ($vmid) = @_;
    my $path = "/tmp/net_pciplot_$vmid";
    return undef if ( !-f $path );

    my $ret = open( TMP, $path );
    if ( !$ret ) {
        ldebug("$$ open  $path failed!\n");
        close(TMP);
        return undef;
    }

    my $virtio = [];
    my $line;
    while ( $line = <TMP> ) {
        chomp $line;
        my @info = split( /,/, $line );
        my $virt = {};
        $virt->{ old }->{ bus }  = $info[0];
        $virt->{ old }->{ addr } = $info[1];
        $virt->{ new }->{ bus }  = $info[2];
        $virt->{ new }->{ addr } = $info[3];
        push @$virtio, $virt;
    } ## end while ( $line = <TMP> )
    close(TMP);

    #使用完即删除改临时文件
    system("rm -f $path");

    return $virtio;
} ## end sub get_virtio_net

sub afterdeal_config_to_command {
    my ( $vmid, $conf, $cmd ) = @_;

#fix td23899 by gzy 2014.9.10。
#下面的判断去掉。即，无论是否安装vtool工具都挂上virtio serial设备。
#但real_use_vchannel字段依旧保留，用于判断安装或卸载vtool之后是否重启了虚拟机
#return if (!defined($conf->{real_use_vchannel})  ||
#   $conf->{real_use_vchannel} ne 'yes');

    #-chardev socket,path=/run/myserial.sock,server,nowait,id=myserial0
    #-device virtio-serial
    #-device virtserialport,chardev=myserial0,name=org.qemu.myserial0.0
    my $path = "/var/run/qemu-server/";

    #mkdir $path if ! -d $path;

    $path .= "$vmid.virtser";
    my $id = 'channelser';
    push @$cmd, '-chardev', "socket,path=${path},server,nowait,id=${id}";
    push @$cmd, '-device',  "virtio-serial,max_ports=2,ioeventfd=off";
    push @$cmd, '-device',  "virtserialport,chardev=${id},name=${id}.virtserial0.0";
} ## end sub afterdeal_config_to_command

my $os_type_windows = 'windows';
my $os_type_linux   = 'linux';

# @note 根据操作系统类型值判断是windows还是linux
# $os_type 配置文件中os_type字段值
# return  windows -windows系统；linux -linux系统；other -其他
my $dist_os_type = sub {
    my $os_type = shift;

    my $os_types_map = os_list_description();
    my $os_name = $os_types_map->{ $os_type // "other" };

    my $type = "Other";
    return $type if ( !$os_name );

    if ( $os_name =~ /windows/i ) {
        $type = $os_type_windows;
    }
    elsif ( $os_name =~ /linux/i ) {
        $type = $os_type_linux;
    }

    return $type;
};